package kim.artemis.rpc.core.protocol.client;

import cn.hutool.core.util.IdUtil;
import kim.artemis.rpc.core.common.enums.LoadBalanceEnum;
import kim.artemis.rpc.core.common.exception.ArtemisException;
import kim.artemis.rpc.core.model.ArtemisRpcFutureResponse;
import kim.artemis.rpc.core.model.ArtemisRpcRequest;
import kim.artemis.rpc.core.model.ArtemisRpcResponse;
import kim.artemis.rpc.core.protocol.serializer.ArtemisSerializer;
import kim.artemis.rpc.core.util.ArtemisUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

/**
 * 引用Bean
 * @author Wq.
 */
@Slf4j
public class ArtemisRpcReferenceBean {


    /**
     * 接口
     */
    @Getter
    private Class<?> classz;

    /**
     * 版本
     */
    @Getter
    private String version;

    /**
     * 服务地址
     */
    @Getter
    private String address;

    /**
     * 超时时间
     */
    @Getter
    private long timeOut = 1000;

    /**
     * 访问令牌
     */
    @Getter
    private String accessToken;

    /**
     * 序列化
     */
    @Getter
    private ArtemisSerializer serializer;

    /**
     * 负载均衡
     */
    @Getter
    private LoadBalanceEnum loadBalanceEnum;

    /**
     * 调用工厂
     */
    @Getter
    private ArtemisRpcClientInvoker invokerFactory;

    public ArtemisRpcReferenceBean(Class<?> classz, String version, String address, long timeOut, String accessToken, ArtemisSerializer serializer, LoadBalanceEnum loadBalanceEnum, ArtemisRpcClientInvoker invokerFactory) {
        this.classz = classz;
        this.version = version;
        this.address = address;
        this.timeOut = timeOut;
        this.accessToken = accessToken;
        this.serializer = serializer;
        this.loadBalanceEnum = loadBalanceEnum;
        this.invokerFactory = invokerFactory;

        if (null == this.classz) {
            throw new ArtemisException("artemis rpc, reference classz missing.");
        }

        if (null == this.serializer) {
            throw new ArtemisException("artemis rpc, reference serializer missing.");
        }

        if (null == this.loadBalanceEnum) {
            throw new ArtemisException("artemis rpc, reference loadBalance missing.");
        }

        if (0 > this.timeOut) {
            this.timeOut = 0;
        }

        if (null == this.invokerFactory) {
            // 获取实例
            this.invokerFactory = ArtemisRpcClientInvoker.getInvokerFactory();
        }

        // 初始化
        initClient();

    }

    private ArtemisRpcClient artemisRpcClient;

    /**
     * 初始化客户端
     */
    public void initClient () {
        try {
            artemisRpcClient = ArtemisRpcClient.class.newInstance();
            artemisRpcClient.init(this);
        } catch (Exception e) {
            throw new ArtemisException(e);
        }
    }

    /**
     * 调用程序
     * @return
     */
    public Object invoker() {
        return Proxy.newProxyInstance(Thread.currentThread()
                        .getContextClassLoader(), new Class<?>[]{ classz },
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 调用处理
                        String className = method.getDeclaringClass().getName();
                        String methodName = method.getName();
                        Class<?>[] parameterTypes = method.getParameterTypes();
                        Object[] parameters = args;

                        if (className.equals(Object.class.getName())) {
                            log.info(">>>>>>> artemis rpc, proxy class-method not support [{}#{}]", className, methodName);
                            throw new ArtemisException("artemis rpc, proxy class-method not support");
                        }

                        // 负载均衡处理 默认轮询模式
                        String socketAddress = address;
                        if ( null == socketAddress || 0 == socketAddress.trim().length() ) {
                            if (null != invokerFactory && null != invokerFactory.getServiceRegistry()) {
                                String serviceKey = ArtemisUtils.serviceKey(className, version);
                                TreeSet<String> addressSet = invokerFactory.getServiceRegistry().discovery(serviceKey);
                                if (null != addressSet || 0 < addressSet.size()) {
                                    if (1 == addressSet.size()) {
                                        socketAddress = addressSet.first();
                                    } else {
                                        socketAddress = loadBalanceEnum.loadBalance.route(serviceKey, addressSet);
                                    }
                                }
                            }
                        }

                        if (null == socketAddress || 0 == socketAddress.trim().length()) {
                            throw new ArtemisException("artemis rpc, reference bean ["+ className +"] socket address empty");
                        }

                        // 初始化数据
                        ArtemisRpcRequest artemisRpcRequest = new ArtemisRpcRequest();
                        artemisRpcRequest.setArtemisId(IdUtil.createSnowflake(5L, 5L).nextId());
                        artemisRpcRequest.setClassName(className);
                        artemisRpcRequest.setMethodName(methodName);
                        artemisRpcRequest.setParameterTypes(parameterTypes);
                        artemisRpcRequest.setParameters(parameters);
                        artemisRpcRequest.setAccessToken(accessToken);
                        artemisRpcRequest.setTimestamp(System.currentTimeMillis());
                        ArtemisRpcFutureResponse futureResponse = new ArtemisRpcFutureResponse(invokerFactory, artemisRpcRequest);

                        // 发送请求
                        try {
                            artemisRpcClient.asyncSend(socketAddress, artemisRpcRequest);
                            ArtemisRpcResponse artemisRpcResponse = futureResponse.get(timeOut, TimeUnit.MILLISECONDS);
                            if (null != artemisRpcResponse.getException()) {
                                throw new ArtemisException(artemisRpcResponse.getException());
                            }
                            // 响应结果
                            return artemisRpcResponse.getResult();
                        } catch (Exception e){
                            futureResponse.removeInvokerFuture();
                            throw new ArtemisException(e);
                        }
                    }
                });
    }


}
